import json
import time

import jwt
import httplib2
import simplejson

import coin.exchange.base.kr_rest.rest_client_base as rcb
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin.exchange.util.rate_limit import RateLimiter
from coin.exchange.util.order_checker import OrderAmountChecker


class UpbitOrderBase(object):
  def __init__(self, key_file):
    key = AuthKey.from_file(key_file)
    self.access_token = key.access_key
    self.secret_key = key.secret_key_bytes
    self.refresh_token = key.refresh_token
    self.rate_limit = RateLimiter(3, 1.0)
    self.api_url = 'https://ccx.upbit.com/api/v1'

  def _nonce(self):
    return str(int(round(time.time() * 1000000)))

  def get_auth(self):
    encoded = jwt.encode({
        'access_key': self.access_token, "nonce": self._nonce()
    },
                         self.secret_key,
                         algorithm='HS256')
    return "Bearer %s" % (encoded.decode("utf-8"))

  def get_response(self, url, request_type, payload):
    headers = {
        'Content-type': 'application/json',
        'Authorization': self.get_auth(),
        'Cache-Control': 'no-cache',
    }
    encoded_payload = json.dumps(payload).encode("utf-8")
    http = httplib2.Http()
    response, content = http.request(self.api_url + "/" + url,
                                     request_type,
                                     headers=headers,
                                     body=encoded_payload)
    return content, response

  def _query_nocheck(self, endpoint, request_type, params=None):
    if params:
      params = {k: v for k, v in params.items() if v is not None}
    req_timestamp = time.time()
    content, header = self.get_response(endpoint, request_type, params)
    res_timestamp = time.time()
    content = simplejson.loads(content)
    return rcb.RestUpdate(req_timestamp, res_timestamp, content, native_header=header)

  def _query(self, endpoint, request_type, params=None, disable_rate_limit=False):
    if not disable_rate_limit:
      while not self.rate_limit.check():
        time.sleep(0.1)
    else:
      self.rate_limit.check()
    return self._query_nocheck(endpoint, request_type, params=params)


class UpbitNativePrivateClient(UpbitOrderBase):
  def __init__(self, key_file):
    UpbitOrderBase.__init__(self, key_file)
    self.order_amount_checker = OrderAmountChecker("KRW", 500, 50000000)

  # practically public
  def query_GET_timestamp(self):
    update = self._query("timestamp", "GET")
    return update

  def query_GET_market_status_all(self):
    update = self._query("market_status/all", "GET")
    return update

  def query_GET_market_status(self, symbol):
    update = self._query("market_status?market=%s" % symbol, "GET")
    return update

  # private
  def query_PUT_kakao_web_auth(self):
    # {"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJhcGkiOnsiYWNjZXNzX2tleSI6Imc1UXJ0enlaNlZHSDZ5MFB2VHBuOVBvSWNtRnh1N01tWUlWV3hoQ0oiLCJzZWNyZXRfa2V5IjoiNXVneDRIaWN4eHBJcDJlR3hzejVzYUNGUWpVZFJObjhpYm1NU28zTCJ9fQ.nHe2hDHT7UHB6fiW8PgZZ65LTpw_gAfgfLVOTZWYZkI","token_type":"jwt","expires_in":3600,"refresh_token":"b9cbd7ddd4335f9252dfaad38f6e7e475ed52d142b52f47dfc3ad234cb9a73e7","scope":"all","created_at":1520653567,"member_uuid":"22358e86-ad91-4ff5-a111-365fc6613ffc"}
    update = self._query("kakao/web/auth?refresh_token=%s" % self.refresh_token, "PUT")
    return update

  def query_GET_investments_assets(self):
    update = self._query("investments/assets", "GET")
    return update

  def query_GET_investments_history(self, symbol, limit=30):
    # ?limit=30&to=b757a1ae-19c1-47cc-8864-05ea32eec933
    update = self._query("investments/history?type=trade&market=%s&limit=%d" % (symbol, limit),
                         "GET")
    return update

  def query_GET_members_me(self):
    update = self._query("members/me", "GET")
    return update

  def query_GET_member_settings(self):
    update = self._query("member_settings", "GET")
    return update

  def query_GET_order_chance(self, symbol):
    update = self._query("members/me/order_chance?market=%s" % symbol, "GET")
    return update

  # order
  def query_GET_orders(self, limit=300):
    # ?market=KRW-XRP&state=wait&limit=300&page=1&order_by=desc
    # ?state=wait&limit=300&page=1&order_by=desc
    update = self._query("orders?state=wait&limit=%d&page=1&order_by=desc" % (limit), "GET")
    return update

  def query_POST_orders(self, symbol, order_type, side, price, qty):
    assert side in ('bid', 'ask')
    assert order_type in ("limit")
    if type(price) == int:
      price = float(price)
    if type(qty) == int:
      qty = float(qty)
    assert type(price) == float and price > 0, 'Invalid price!'
    assert type(qty) == float and qty > 0, 'Invalid qty!'
    self.order_amount_checker.check_amount("KRW", price, qty)
    update = self._query("orders",
                         "POST",
                         params=dict(market=symbol,
                                     ord_type=order_type,
                                     price=price,
                                     side=side,
                                     volume=qty))
    return update

  def query_GET_order(self, uuid):
    update = self._query("order?uuid=%s" % uuid, "GET")
    return update

  def query_DELETE_order(self, uuid):
    update = self._query("order?uuid=%s" % uuid, "DELETE")
    return update
